﻿using AntJoin.Core.Utils;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;

namespace AntJoin.Redis
{
    public class RedisHelper
    {
        private static ConnectionMultiplexer _conn = RedisUtil.GetFactionConn();
        private static IDatabase _cacheDb = _conn.GetDatabase();
        private static IServer _server = _conn.GetServer(_conn.GetEndPoints()[0]);
        private static readonly Encoding TextEncoding = Encoding.UTF8;

        private static RedisHelper _instance = null;
        private static readonly object Padlock = new object();

        RedisHelper()
        {
        }

        public static RedisHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (Padlock)
                    {
                        if (_instance == null)
                        {
                            _instance = new RedisHelper();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// 获取系统的redis key前缀
        /// </summary>
        /// <param name="resourceid">资源Id</param>
        /// <returns></returns>
        public static string GetMyKey(string resourceid = "")
        {
            string key = "report_";
            if (!string.IsNullOrWhiteSpace(resourceid))
            {
                key = string.Format("report_res_{0}", resourceid);
            }
            return key;
        }

        #region String 可以设置过期时间

        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public static bool SetValue(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return _cacheDb.StringSet(key, value, expiry);
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="arr">key</param>
        /// <returns></returns>
        public static bool SetValue(KeyValuePair<RedisKey, RedisValue>[] arr)
        {
            return _cacheDb.StringSet(arr);
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool SetValue<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
            string json = JsonSerializer.Serialize(obj);
            return _cacheDb.StringSet(key, json, expiry);
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>

        public static RedisValue GetValue(string key)
        {
            return _cacheDb.StringGet(key);
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public static RedisValue[] GetValues(List<RedisKey> listKey)
        {
            return _cacheDb.StringGet(listKey.ToArray());
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetValue<T>(string key)
        {
            var val = _cacheDb.StringGet(key);
            if (val.IsNullOrEmpty)
            {
                return default;
            }
            return JsonSerializer.Deserialize<T>(_cacheDb.StringGet(key));
        }

        /// <summary>
        /// 追加值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void StringAppend(string key, string value)
        {
            ////追加值，返回追加后长度
            long appendlong = _cacheDb.StringAppend(key, value);
        }
        #endregion

        #region Hash 操作

        /// <summary>
        /// 获取hash长度
        /// </summary>
        /// <param name="hashId"></param>
        /// <returns></returns>
        public static long Hlen(string hashId)
        {
            return _cacheDb.HashLength(hashId);
        }
        /// <summary>
        /// 设置hash field为指定值，如果key不存在，则先创建
        /// </summary>
        /// <param name="hashId"></param>
        /// <param name="datakey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool Hset(string hashId, string datakey, string value)
        {
            return _cacheDb.HashSet(hashId, datakey, value);
        }

        /// <summary>
        /// 设置hash field为指定值，如果key不存在，则先创建
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="hashId"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool Hset<T>(string hashId, string key, T value)
        {
            return Hset(hashId, key, JsonSerializer.Serialize(value));
        }
        /// <summary>
        /// 保存一个集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="list">数据集合</param>
        /// <param name="getModelId"></param>
        public static void Hset<T>(string key, List<T> list, Func<T, string> getModelId)
        {
            List<HashEntry> listHashEntry = new List<HashEntry>();
            foreach (var item in list)
            {
                string json = JsonSerializer.Serialize(item);
                listHashEntry.Add(new HashEntry(getModelId(item), json));
            }
            _cacheDb.HashSet(key, listHashEntry.ToArray());
        }

        /// <summary>
        /// 获取Hash中的单个key的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hasFildValue"></param>
        /// <returns></returns>
        public static RedisValue Hget(string key, string hasFildValue)
        {
            return _cacheDb.HashGet(key, hasFildValue);
        }

        /// <summary>
        /// 获取Hash中的单个key的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="hasFildValue">RedisValue</param>
        /// <returns></returns>
        public static T Hget<T>(string key, string hasFildValue)
        {
            if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(hasFildValue))
            {
                RedisValue value = _cacheDb.HashGet(key, hasFildValue);
                if (!value.IsNullOrEmpty)
                {
                    return JsonSerializer.Deserialize<T>(value);
                }
            }
            return default(T);
        }

        /// <summary>
        /// 获取hash中的多个key的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">Redis Key</param>
        /// <param name="listhashFields">RedisValue value</param>
        /// <returns></returns>
        public static List<T> Hgets<T>(string key, List<RedisValue> listhashFields)
        {
            List<T> result = new List<T>();
            if (!string.IsNullOrWhiteSpace(key) && listhashFields.Count > 0)
            {
                RedisValue[] value = _cacheDb.HashGet(key, listhashFields.ToArray());
                foreach (var item in value)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        result.Add(JsonSerializer.Deserialize<T>(item));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有Redis key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> Hkeys<T>(string key)
        {
            List<T> result = new List<T>();
            RedisValue[] arr = _cacheDb.HashKeys(key);
            foreach (var item in arr)
            {
                if (!item.IsNullOrEmpty)
                {
                    result.Add(JsonSerializer.Deserialize<T>(item));
                }
            }
            return result;
        }

        /// <summary>
        /// 获取hash表的所有字段
        /// </summary>
        /// <param name="hashId"></param>   
        /// <returns></returns>
        public static string[] Hkeys(string key)
        {
            RedisValue[] arr = _cacheDb.HashKeys(key);
            string[] result = new string[arr.Length];
            for (int i = 0; i < arr.Length; i++)
            {
                result[i] = arr[i];
            }
            return result;
        }

        /// <summary>
        /// 获取hashkey所有的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> HgetAll<T>(string key)
        {
            List<T> result = new List<T>();
            HashEntry[] arr = _cacheDb.HashGetAll(key);
            foreach (var item in arr)
            {
                if (!item.Value.IsNullOrEmpty)
                {
                    result.Add(JsonSerializer.Deserialize<T>(item.Value));
                }
            }
            return result;
        }

        /// <summary>
        /// 删除hasekey
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashField"></param>
        /// <returns></returns>
        public static bool Hdel(RedisKey key, RedisValue hashField)
        {
            return _cacheDb.HashDelete(key, hashField);
        }

        /// <summary>
        /// 查看哈希表 key 中，给定域 hashField 是否存在。
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashField"></param>
        /// <returns></returns>
        public static bool Hexists(RedisKey key, RedisValue hashField)
        {
            return _cacheDb.HashExists(key, hashField);
        }

        #endregion

        #region key
        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public static bool DelKey(string key)
        {
            return _cacheDb.KeyDelete(key);
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public static long DelKeys(RedisKey[] keys)
        {
            return _cacheDb.KeyDelete(keys);
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public static long DelKeys(List<string> keys)
        {
            List<RedisKey> rkeys = new List<RedisKey>();
            foreach (var key in keys)
            {
                rkeys.Add(key);
            }
            return _cacheDb.KeyDelete(rkeys.ToArray());
        }

        /// <summary>
        /// 判断key是否存在
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public static bool ExitsKey(string key)
        {
            return _cacheDb.KeyExists(key);
        }

        /// <summary>
        /// 重新命名key
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public static bool RenameKey(string key, string newKey)
        {
            return _cacheDb.KeyRename(key, newKey);
        }
        #endregion

        #region 整型 操作
        /// <summary>
        /// 对key的值做加加操作,Increment一个不存在的key，则设置key为1
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long IncrValue(string key)
        {
            return _cacheDb.StringIncrement(key);
        }

        /// <summary>
        /// 加指定值,key不存在时候会设置key,并认为原来的value是0
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long IncrValue(string key, long value)
        {
            return _cacheDb.StringIncrement(key, value);
        }

        /// <summary>
        /// 减减操作，Decrement一个不存在key，则设置key为-1
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long DecrValue(string key)
        {
            return _cacheDb.StringDecrement(key);
        }

        /// <summary>
        /// 减指定值,key不存在时候会设置key,并认为原来的value是0
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long DecrValue(string key, long value)
        {
            return _cacheDb.StringDecrement(key, value);
        }
        #endregion

        #region List 操作
        /// <summary>
        /// 返回key对应list的长度，key不存在返回0,如果key对应类型不是list返回错误
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long LGetLen(string key)
        {
            return _cacheDb.ListLength(key);
        }

        /// <summary>
        ///  返回指定区间内的元素，下标从0开始，负值表示从后面计算，-1表示倒数第一个元素 ，key不存在返回空列表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="fromIndex"></param>
        /// <param name="toIndex"></param>
        /// <returns></returns>
        public static string[] LRange(string key, long fromIndex, long toIndex)
        {

            RedisValue[] rvalues = _cacheDb.ListRange(key, fromIndex, toIndex);

            string[] values = new string[rvalues.Length];

            for (int i = 0; i < rvalues.Length; i++)
            {
                values[i] = TextEncoding.GetString(rvalues[i]);
            }
            return values;
        }

        /// <summary>
        /// 返回指定区间内的元素，下标从0开始
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="fromIndex"></param>
        /// <param name="toIndex"></param>
        /// <returns></returns>
        public static List<T> LRange<T>(string key, long fromIndex, long toIndex)
        {

            List<T> tList = new List<T>();

            RedisValue[] rvalues = _cacheDb.ListRange(key, fromIndex, toIndex);

            for (int i = 0; i < rvalues.Length; i++)
            {
                tList.Add(JsonSerializer.Deserialize<T>(TextEncoding.GetString(rvalues[i])));
            }

            return tList;
        }

        /// <summary>
        /// 返回指定区间内的元素，下标从0开始
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="fromIndex"></param>
        /// <returns></returns>
        public static T LRange<T>(string key, int fromIndex)
        {

            List<T> tList = new List<T>();

            RedisValue[] rvalues = _cacheDb.ListRange(key, fromIndex, fromIndex + 1);

            for (int i = 0; i < rvalues.Length; i++)
            {
                tList.Add(JsonSerializer.Deserialize<T>(TextEncoding.GetString(rvalues[i])));
            }

            if (tList != null)
                return tList[0];
            return default(T);

        }
        /// <summary>
        /// 在key对应list的头部添加字符串元素，返回列表行数
        /// </summary>
        /// <param name="key"></param>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static long LPush(string key, string keyValue)
        {
            return _cacheDb.ListLeftPush(key, keyValue);
        }

        /// <summary>
        /// 在key对应list的头部添加字符串元素，返回列表行数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long LPush<T>(string key, T value)
        {
            return _cacheDb.ListLeftPush(key, JsonSerializer.Serialize(value));
        }
        /// <summary>
        /// 在key对应list的头部添加字符串元素，返回列表行数,批量加入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static long LPush<T>(string key, List<T> values)
        {
            long affected = 0;
            foreach (var value in values)
            {
                affected = _cacheDb.ListLeftPush(key, JsonSerializer.Serialize(value));
            }
            return affected;
        }

        /// <summary>
        /// 设置list中指定下标的元素值，成功返回1，key或者下标不存在返回错误
        /// </summary>
        /// <param name="key"></param>
        /// <param name="listIndex"></param>
        /// <param name="keyValue"></param>
        public static void LSet(string key, int listIndex, string keyValue)
        {
            _cacheDb.ListSetByIndex(key, listIndex, keyValue);
        }

        /// <summary>
        ///  在key对应list的尾部添加字符串元素，返回1表示成功，0表示key存在且不是list类型
        /// </summary>
        /// <param name="key"></param>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static long RPush(string key, string keyValue)
        {
            return _cacheDb.ListRightPush(key, keyValue);
        }

        /// <summary>
        /// 在key对应list的尾部添加字符串元素，返回1表示成功，0表示key存在且不是list类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long RPush<T>(string key, T value)
        {
            return _cacheDb.ListRightPush(key, JsonSerializer.Serialize(value));
        }

        /// <summary>
        ///  在key对应list的尾部添加字符串元素，返回1表示成功，0表示key存在且不是list类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static long RPush<T>(string key, List<T> values)
        {
            long affected = 0;
            foreach (var value in values)
            {
                affected = _cacheDb.ListRightPush(key, JsonSerializer.Serialize(value));
            }
            return affected;
        }

        /// <summary>
        /// 从list的头部删除元素，并返回删除元素。如果key对应list不存在或者是空返回nil，如果key对应值不是list返回错误
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string LPop(string key)
        {
            string value = _cacheDb.ListLeftPop(key);
            if (value != null)
            {
                return value;
            }
            else
            {
                return "";
            }
        }

        /// <summary>
        /// 从list的头部删除元素，并返回删除元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T LPop<T>(string key) where T : new()
        {

            string value = _cacheDb.ListLeftPop(key);
            if (value != null)
            {
                return JsonSerializer.Deserialize<T>(value);
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 从list的尾部删除元素，并返回删除元素
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string RPop(string key)
        {
            string value = _cacheDb.ListRightPop(key);
            if (value != null)
            {
                return value;
            }
            else
            {
                return "";
            }
        }

        /// <summary>
        /// 从list的尾部删除元素，并返回删除元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listId"></param>
        /// <returns></returns>
        public static T RPop<T>(string listId) where T : new()
        {

            string value = _cacheDb.ListLeftPop(listId);
            if (value != null)
            {
                return JsonSerializer.Deserialize<T>(value);
            }
            else
            {
                return default(T);
            }

        }

        /// <summary>
        /// 从key对应list中删除count个和value相同的元素。count为0时候删除全部
        /// </summary>
        /// <param name="key"></param>
        /// <param name="keyValue"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static long LRem(string key, string keyValue, long count)
        {
            return _cacheDb.ListRemove(key, keyValue, count);
        }
        #endregion

        #region 分布式锁

        /// <summary>
        /// 创建缓存锁
        /// </summary>
        /// <param name="lockKey"></param>
        /// <param name="lockValue"></param>
        /// <param name="minutes"></param>
        /// <returns></returns>
        public static bool LockTake(string lockKey, string lockValue, int minutes)
        {
            return _cacheDb.LockTake(lockKey, lockValue, new TimeSpan(0, minutes, 0));
        }

        /// <summary>
        /// 释放缓存锁
        /// </summary>
        /// <param name="lockKey"></param>
        /// <param name="lockValue"></param>
        /// <returns></returns>
        public static bool LockRelease(string lockKey, string lockValue)
        {
            return _cacheDb.LockRelease(lockKey, lockValue);
        }

        #endregion

        /// <summary>
        /// 模糊查找Key
        /// </summary>
        /// <param name="pattern"></param>
        /// <param name="database"></param>
        /// <returns></returns>
        public static RedisKey[] SearchKeys(string pattern, int database = 0)
        {
            var keys = _server.Keys(database, pattern).ToArray();
            return keys;
        }

        /// <summary>
        /// 为给定 key 设置生存时间，当 key 过期时(生存时间为 0 )，它会被自动删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool KeyExpire(string key, TimeSpan expiry)
        {
            return _cacheDb.KeyExpire(key, expiry);
        }

        /// <summary>
        /// 为给定 key 设置生存时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool KeyExpire(string key, DateTime expiry)
        {
            return _cacheDb.KeyExpire(key, expiry);
        }
    }
}
